#ifndef SERIALINATOR_H
#define SERIALINATOR_H

#include <string>
#include <bitset>
#include <math.h>
#include "../BTree/IRegistro.h"
#include "../utils/GammaContainer.h"


class SerialInator {
	private:
		int _posActual;
		unsigned int _cantBytes;

		static const unsigned int tamBits_ulong = sizeof(unsigned long int) * 8;

		template<unsigned long int N>
		std::bitset<SerialInator::tamBits_ulong> getMapaBitsParcial(std::bitset<N> mapaBits, int porcion) const;

		template<unsigned long int N>
		void setMapaBitsParcial(std::bitset<N> &mapaBits, std::bitset<SerialInator::tamBits_ulong> bsPorcion, int porcion) const;

	public:
		SerialInator();
		~SerialInator();

		// Permite cambiar el modo de serializar a des-serializar o visceversa
		void reset();

		// Devuelve la cantidad de bytes en el buffer
		unsigned int getBufferSize() const;

		// Serializa cada tipo de dato de la forma correcta
		void serializar(char* &arrBytes, char c);
		void serializar(char* &arrBytes, int valor);
		void serializar(char* &arrBytes, unsigned int valor);
		void serializar(char* &arrBytes, unsigned short int valor);
		void serializar(char* &arrBytes, unsigned long int valor);
		void serializar(char* &arrBytes, std::string cadena);
		void serializar(char* &arrBytes, IRegistro* reg);
		void serializar(char* &arrBytes, GammaContainer &gc);

		// Des-serializa los tipos de datos del buffer
		void desSerializar(char* arrBytes, char &c);
		void desSerializar(char* arrBytes, int &valor);
		void desSerializar(char* arrBytes, unsigned int &valor);
		void desSerializar(char* arrBytes, unsigned short int &valor);
		void desSerializar(char* arrBytes, unsigned long int &valor);
		void desSerializar(char* arrBytes, std::string &cadena);
		void desSerializar(char* arrBytes, IRegistro* &reg);
		void desSerializar(char* arrBytes, GammaContainer &gc);

		// Para manejar la (des)serializacion de los mapas de bits
		template<unsigned long int N>
		void serializar(char* &arrBytes, std::bitset<N> mapaBits);

		template<unsigned long int N>
		void desSerializar(char* arrBytes, std::bitset<N> &mapaBits);

		// Para saber el tamaño de un string que se serializar
		static unsigned int getSizeOf(std::string s);
};

//---------------------------------------------------------------------------


template<unsigned long int N>
void SerialInator::serializar(char* &arrBytes, std::bitset<N> mapaBits){
	// Cuantos "unsigned long int" hay que guardar en la memoria
	int cantNros = ceil( static_cast<double>(N / SerialInator::tamBits_ulong) );

	for (int i = 0; i < cantNros; ++i){
		std::bitset<SerialInator::tamBits_ulong> bsTemp = getMapaBitsParcial<N>(mapaBits, i);
		this->serializar(arrBytes, bsTemp.to_ulong());
	}
}


template<unsigned long int N>
std::bitset<SerialInator::tamBits_ulong> SerialInator::getMapaBitsParcial(std::bitset<N> mapaBits, int porcion) const{
	int posInicial = porcion * SerialInator::tamBits_ulong;
	int posFinal = (porcion +1) * SerialInator::tamBits_ulong;

	std::bitset<SerialInator::tamBits_ulong> bsTemp;
	for (int i = posInicial; i < posFinal; ++i){
		bsTemp[i - posInicial] = mapaBits[i];
	}

	return bsTemp;
}


template<unsigned long int N>
void SerialInator::setMapaBitsParcial(std::bitset<N> &mapaBits, std::bitset<SerialInator::tamBits_ulong> bsPorcion, int porcion) const{
	int posInicial = porcion * SerialInator::tamBits_ulong;
	int posFinal = (porcion +1) * SerialInator::tamBits_ulong;

	for (int i = posInicial; i < posFinal; ++i){
		mapaBits[i] = bsPorcion[i - posInicial];
	}
}


template<unsigned long int N>
void SerialInator::desSerializar(char* arrBytes, std::bitset<N> &mapaBits){
	int cantNros = ceil( static_cast<double>(N / SerialInator::tamBits_ulong) );

	for (int i = 0; i < cantNros; ++i){
		unsigned long int porcionBits = 0;
		this->desSerializar(arrBytes, porcionBits);
		std::bitset<SerialInator::tamBits_ulong> bsTemp(porcionBits);

		setMapaBitsParcial<N>(mapaBits, bsTemp, i);
	}

}


#endif

